package com.coderman.core;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.Lazy;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.*;

/**
 * @author coderman @Title: TODO @Description: TOD
 * @date 2022/1/25 19:48
 */
@Component
@Lazy(value = false)
public class ConstantService implements InitializingBean {

  /** 存放常量 */
  private static final Map<String, List<ConstantItem>> CONST_MAP = new HashMap<>();

  /** 读取器工厂 */
  private static final CachingMetadataReaderFactory READER_FACTORY =
      new CachingMetadataReaderFactory();

  /** 日志打印 */
  private static final Logger logger = LoggerFactory.getLogger(ConstantService.class);

  static {
    // 扫描常量class
    Set<BeanDefinition> constantBeanSet = getConstantBeanSet();
    for (BeanDefinition beanDefinition : constantBeanSet) {
      try {
        // 是否允许冲突：即本项目中存在group, 其他项目也存在group的情况.
        boolean allowedConflict = true;
        Constant constant =
            Class.forName(beanDefinition.getBeanClassName()).getDeclaredAnnotation(Constant.class);
        if (null != constant) {
          allowedConflict = constant.allowedConflict();
        }
        // 获取中的属性
        Field[] declaredFields =
            Class.forName(beanDefinition.getBeanClassName()).getDeclaredFields();
        for (Field declaredField : declaredFields) {
          Annotation[] declaredAnnotations = declaredField.getDeclaredAnnotations();
          for (Annotation declaredAnnotation : declaredAnnotations) {
            if (declaredAnnotation instanceof ConstantList) {
              String group = ((ConstantList) declaredAnnotation).group();
              String name = ((ConstantList) declaredAnnotation).name();
              List<ConstantItem> constantItems;
              // 如果是其他微服务的常量,即通过api引入进来的,并且该常量类不允许冲突,那么就不添加该组常量
              if (otherProject(beanDefinition) && !allowedConflict) {
                continue;
              }
              if (CONST_MAP.containsKey(group)) {
                constantItems = CONST_MAP.get(group);
              } else {
                constantItems = new ArrayList<>();
                CONST_MAP.put(group, constantItems);
              }
              constantItems.add(new ConstantItem((String) declaredField.get(null), name));
            }
          }
        }
        logger.info("扫描常量:{}", beanDefinition.getBeanClassName());
      } catch (ClassNotFoundException | IllegalAccessException e) {
        logger.error(e.getMessage(), e);
      }
    }
  }

  /**
   * 其他项目的常量
   *
   * @param beanDefinition
   * @return
   */
  private static boolean otherProject(@NotNull BeanDefinition beanDefinition) {
    // TODO 具体可以通过自己本项目的域名来判断, 例如: com.lcsc.pay.constant, com.lcsc.order.constant,
    // com.lcsc.member.constant
    return false;
  }

  /**
   * 获取所有常量
   *
   * @return
   */
  public static Map<String, List<ConstantItem>> getAllConst() {
    return cloneConstMap();
  }

  /**
   * 克隆一个常量map
   *
   * @return
   */
  private static Map<String, List<ConstantItem>> cloneConstMap() {
    Map<String, List<ConstantItem>> map = new HashMap<>();
    for (Map.Entry<String, List<ConstantItem>> entry : ConstantService.CONST_MAP.entrySet()) {
      List<ConstantItem> constantItems = new ArrayList<>();
      for (ConstantItem constantItem : entry.getValue()) {
        constantItems.add(new ConstantItem(constantItem.getCode(), constantItem.getName()));
      }
      map.put(entry.getKey(), constantItems);
    }
    return map;
  }

  private static Set<BeanDefinition> getConstantBeanSet() {
    Set<BeanDefinition> constBeanSet = new HashSet<>();
    try {
      ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
      Resource[] resources =
          resourcePatternResolver.getResources("classpath*:com/coderman/**/constant/*.class");
      for (Resource resource : resources) {
        if (resource.isReadable()) {
          MetadataReader metadataReader = READER_FACTORY.getMetadataReader(resource);
          ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
          sbd.setResource(resource);
          sbd.setSource(resource);
          constBeanSet.add(sbd);
        }
      }
      return constBeanSet;
    } catch (IOException e) {
      logger.error(e.getMessage(), e);
    }
    return constBeanSet;
  }

  @Override
  public void afterPropertiesSet() {
    // TODO
    System.out.println("加载完成之后,将常量保存到redis中......");
  }
}
